
package taller2.controller.modes.supervisor;

import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Stack;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import taller2.controller.modes.Mode;
import taller2.interfaces.Connection;
import taller2.interfaces.Graph;
import taller2.interfaces.Node;
import taller2.utils.Constants.Colores;
import taller2.utils.Constants;
import taller2.utils.Point;
import taller2.view.draws.DrawingArea;
import taller2.view.draws.GraphDrawer;

public class CycleDetectionSupervisorMode implements Mode {
	private Graph graph;
	private Node actual,start;

	Stack<Node> stack; 
	
	private boolean finished;
	
	private static final String okMsg2 ="there were no cycles in the graph.";
	
	// constructor
	public CycleDetectionSupervisorMode(Graph graph){
		this.graph = graph;
		this.actual = null;
		
		this.start = getStartingNode();

		this.stack = new Stack<Node>();
		this.stack.add(start);

		this.finished = false;
		

	}
	
	private void addConnectedNodes(ArrayList<Node> array,Node node) {
		for(Connection connection : node.getConnections()){
			Node other = connection.getOtherNode();
			
			if(!wasPassed(other) && !other.equals(start) && !other.isSelected() && !array.contains(other))
				array.add(other);
		}
	}

	private Node getStartingNode(){
		ArrayList<Node> nodes = graph.getNodes();
		for (Node node : nodes){
			Colores color = node.getColor();
			if (color.compareTo(Colores.green) == 0)
				return node;
		}
		Node strt = nodes.get(0);
		strt.changeColor(Colores.green);
		return strt;
	}
	
	public void whenPressed(MouseEvent arg0, DrawingArea area) {
		GraphDrawer drawer = area.getDrawer();
		Point pos = new Point(arg0.getX(),arg0.getY());
		if(!finished)
			supervise(drawer,area,pos);
	}
	
	private void supervise(GraphDrawer drawer, DrawingArea area,Point pos){
		actual = drawer.getCloseByNode(pos);
		if(actual != null && !actual.equals(start))
			processSolution();

		Stack<Node> cycle = getCycle();
		boolean cycleFound = (cycle != null);
		if(cycleFound)
			selectConnections(cycle);
		
		if(cycleFound || allNodesSelected()){
			JFrame frame = new JFrame();
			JOptionPane.showMessageDialog(frame,Constants.okMsg);

			finished = true;
		}else{
			if(allNodesPassed()){
				start.select();
				start = null;
				unpassNodes();
				
				if(start != null){
					stack.clear();
					for(Node node : graph.getNodes()){
						if(node.isSelected())
							stack.add(node);							
					}					
					stack.add(start);
				}else{
					JFrame frame = new JFrame();
					JOptionPane.showMessageDialog(frame,okMsg2);					
					finished = true;
				}
					
			}
		}
				
		area.repaint();
	}

	private Stack<Node> getCycle() {
		Stack<Node> cycle = new Stack<Node>();
		cycle.add(start);
		
		for(Connection conn : start.getConnections()){
			Node other = conn.getOtherNode();
			if(wasPassed(other) && getCycle(start,other,cycle))		
				return cycle;
		}
		return null;
	}

	private boolean getCycle(Node strt, Node act, Stack<Node> cycle) {
		if(wasPassed(act)|| act.equals(start))
			cycle.add(act);
		
		if (act.equals(strt))
			return true;
		
		for(Connection conn : act.getConnections()){
			Node other = conn.getOtherNode();
			
			if(wasPassed(other) || other.equals(start)){
				if(getCycle(strt,other,cycle))
					return true;
			}
		}
		cycle.remove(act);
		return false;
	}

	private void selectConnections(Stack<Node> cycle) {
		System.out.printf("\ncycle ");
		for(Node node : cycle){
			String tag = node.getTag();
			if (tag == null) { tag = Constants.sinTag;}
			System.out.printf("%s ",tag);
		}
		
		
		int tam = cycle.size();
		for(int i=0 ; i<tam; i++){
			Node node1 = cycle.get(i);
			Node node2 = cycle.get((i+1)%tam);

			node1.selectConnectionTo(node2);
		}			
	}
	
	private void processSolution(){
		ArrayList<Node> posibleSolutions = new ArrayList<Node>();
				
		if(stack.isEmpty() && posibleSolutions.isEmpty()){	 // caso nodos sueltos
			actual.changeColor(Colores.purple); 
			stack.add(actual);
		}else{
			do{												// obtengo sol posibles
				Node node = stack.peek();
				addConnectedNodes(posibleSolutions,node);
				
				if(posibleSolutions.isEmpty())
					stack.pop();
				
				if(stack.isEmpty() && posibleSolutions.isEmpty()){
					actual.changeColor(Colores.purple);
					stack.add(actual);
					return;
				}
				
			}while(posibleSolutions.isEmpty());
			
			for(Node node : posibleSolutions){						// me fijo si el q elijio es correcto
				if((actual != null) && (actual.equals(node))){
					stack.add(actual);
					actual.changeColor(Colores.purple);
					return;
				}				
			}
		
			JFrame frame = new JFrame();
			JOptionPane.showMessageDialog(frame,Constants.errorMsg);			
		}
	}
	
	private boolean wasPassed(Node node){
		 Colores color = node.getColor();
		if(color.compareTo(Colores.purple) == 0)
			return true;
		return false;
	}

	private void unpassNodes() {
		for(Node node : graph.getNodes()){
			if(wasPassed(node)){
				node.changeColor(Colores.yellow);
			}
			
			if((start == null) && (!node.isSelected())){
				start = node;
				start.changeColor(Colores.green);
			}
		}
	}
	
	private boolean allNodesPassed() {
		for(Node node : graph.getNodes()){
			if(!node.isSelected() && !wasPassed(node) && !node.equals(start))
				return false;
		}
		return true;
	}
	
	private boolean allNodesSelected() {
		for(Node node : graph.getNodes()){
			if(!node.isSelected())
				return false;
		}
		return true;
	}
	
	
	public void whenReleased(MouseEvent arg0, DrawingArea area) {}

	public void whenDragged(MouseEvent arg0, DrawingArea area) {}

}
